/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package client.network;

import client.logic.ClientLogicImplement;
import com.cse.p2p.common.CommonSettings;
import java.io.*;
import java.net.*;
import com.cse.p2p.common.*;
import com.cse.p2p.database.*;
import com.cse.p2p.hash.*;
import com.cse.p2p.localfile.*;
import com.cse.p2p.parser.*;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import client.logic.ClientLogicImplement.*;
import clientside.DataClientSide;
import java.util.ArrayList;

/**
 *
 * @author TuyetNgan
 */
public class ClientControlTask_Client extends Thread {

	private int portControl;
	private Socket clientSocket = null;
	private BufferedReader in = null;
	private PrintStream out = null;
	private String IPconnect;
	private PieceInfo pieceInfo = null;
	private DicFile dicFile = null;
	private Integer fileID = null;
	private boolean isReceived = false;
	private boolean isClose = false;
	private DataClientSide dataThread;
	private volatile boolean isWaitingForPiece = true;
	private volatile boolean isRunning = true;

	public ClientControlTask_Client( String ip, int p, DicFile dic, PieceInfo pI ) {
		this.IPconnect = ip;
		this.portControl = p;
		this.dicFile = dic;
		this.pieceInfo = pI;
	}

	public ClientControlTask_Client( String ip, int p, DicFile dic, PieceInfo pI, DataClientSide thread ) {
		this.IPconnect = ip;
		this.portControl = p;
		this.dicFile = dic;
		this.pieceInfo = pI;
		this.dataThread = thread;
	}

	public void newChat() {
		try {
			clientSocket = new Socket( IPconnect, portControl );
			out = new PrintStream( new DataOutputStream( clientSocket.getOutputStream() ) );
			in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream() ) );
		}
		catch ( IOException e ) {
			System.err.println( "NETWORK: ERROR: Cannot connect to the client at " + IPconnect + " on " + portControl);
		}
	}

	public void closeConnection() {
		try {
			out.close();
			in.close();
			clientSocket.close();
		}
		catch ( IOException e ) {
		}

	}

	public synchronized  boolean getisReceived() {
		return isReceived;
	}

	public List<PeerInfo> updatePeerInfo( List<PeerInfo> info ) throws Exception {
		List<PeerInfo> chosenPiecesList = new ArrayList<PeerInfo>();
		PeerInfo currentPeerInfo;
		PeerInfo chosenPeerInfo;
		for ( int i = 0; i < info.size(); i++ ) {
			currentPeerInfo = info.get( i );
			int[] currenPieces = currentPeerInfo.getPieces();
//			int[] chosenPieces = new int[ currenPieces.length ];
			List<Integer> chosenPieces = new ArrayList<Integer>();
			int count = 0;
			for ( int j = 0; j < currenPieces.length; j++ ) {
				System.out.print("DATABASE: check piece " + currenPieces[j] + " of " + fileID + " ... ");
				try {
					if ( !ClientDatabase.checkPieceExist( currenPieces[j], fileID ) ) {
						chosenPieces.add( currenPieces[j]);
						System.out.println( "Availabel!");
					} else {
						System.out.println( "Exist!");
					}
					
				}
				catch ( SQLException ex ) {
					System.out.println( "SQLError: " + ex.getMessage() );
					ex.printStackTrace();
				}
				catch ( Exception e ) {
					System.out.println( "General Error: " + e.getMessage() );
					e.printStackTrace();
				}
			}
			int[] temp = new int[ chosenPieces.size()];
			for (int k = 0; k < chosenPieces.size(); k++){
				temp[k] = chosenPieces.get( k );
			}
			chosenPeerInfo = new PeerInfo( currentPeerInfo.getFileID(),temp , currentPeerInfo.getIP() );
			chosenPiecesList.add( chosenPeerInfo );
		}
        
		return chosenPiecesList;
	}

	public void waiting() throws Exception {
		try {
			String mess, data;
			Message message;
//			while ( ( mess = in.readLine() ) != null ) {
			mess = in.readLine();
			message = Message.fromString( mess );
			int typeOfProtocol = message.getType();
			data = message.getData();

			switch ( typeOfProtocol ) {
				//Server -> Client: the info of all available pieces of a file
				case Message.MsgType.RES_FOR_AVAI_PIECES: {
					System.out.println( "NETWORK: RESPONSE FROM SERVER: list of avai peer" );
					/* Call Database to know which piece client need */
					List<PeerInfo> info = PeerInfo.listFromString( data );
					List<PeerInfo> chosenPiecesList = updatePeerInfo( info );
					Map hashMap = dicFile.getPieceHash();
					int[] neededPieces = ClientLogicImplement.neededPieces( dicFile );
					int count = 0;

					/* Ask all peer online to download piece */
//					while ( count < hashMap.size() || this.isRunning) { // download enough piece 
					while ( count < neededPieces.length && this.isRunning) { // download enough piece 
                                /* Client calculate to choose suitable peer and piece */
						System.out.println( "Peer list FIRST " + chosenPiecesList );
						String[] peerChosen = ClientLogicImplement.findPieceAndPeer( chosenPiecesList,
																					 Integer.toString( dicFile.getFileID() ) );
						System.out.println( "LOGIC: CHOOSE PIECE: prepare to get {" + peerChosen[0] + ", " + peerChosen[1] + "," + peerChosen[2] + "}" );
						PieceInfo piece = new PieceInfo( Integer.parseInt( peerChosen[0] ), Integer.parseInt( peerChosen[1] ) );
						String IPrequest = peerChosen[2];
						Thread requestPiece = new ClientControlTask_Client( IPrequest,
																			CommonSettings.CLIENT_SERVERSOCKET,
																			dicFile,
																			piece );
						dataThread.registerNewDownload( dicFile.getFileID(), this );
						requestPiece.start();
						while ( requestPiece.isAlive() );
						while ( this.isWaitingForPiece );
						count++;
						System.out.println( "Got a package number  "+count);
						this.isWaitingForPiece = true;
						System.out.println( "Peer list BEFORE " + chosenPiecesList );
						chosenPiecesList = updatePeerInfo( chosenPiecesList );
//						neededPieces = ClientLogicImplement.neededPieces( dicFile );
						System.out.println( "Peer list AFTER " + chosenPiecesList );
					}
					isReceived = true;
					break;
				}
				// one Peer -> Client: reponse for the request of the client    
				case Message.MsgType.RES_ASK_A_PIECE: {
					if ( data.equals( "OK" ) ) {
						isClose = true;
					}
					else if ( data.equals( "NONE" ) ) {
						throw new Exception( "Peer has not that piece any more" );
					}
					else {
						throw new Exception( "Peer is offline" );
					}

					break;
				}
				case Message.MsgType.RES_REG_NICK: {
					System.out.println( "Server success register!" );
				}
			}

		}
		catch ( IOException ex ) {
			System.out.println( "NETWORK: CONTROL-SERVER: error" );
			ex.printStackTrace();
		}
	}

	@Override
	public void run() {
		newChat();
		if ( dicFile != null && pieceInfo == null ) { // Client -> Server : request download file
			fileID = dicFile.getFileID();
			Message mess = new Message( "GET", "LIST", Integer.toString( fileID ) );
			out.println( mess.toString() );
			try {
				waiting();
			}
			catch ( Exception ex ) {
				Logger.getLogger( ClientControlTask_Client.class.getName() ).log( Level.SEVERE, null, ex );
			}
		}
		else if ( dicFile != null && pieceInfo != null ) { // Client-> one peer : dicFile and peerInfoList != null   
			Message messSend = new Message( "GET", "PIECE", pieceInfo.toString() );
			out.println( messSend.toString() );
			try {
				waiting();
			}
			catch ( Exception ex ) {
				Logger.getLogger( ClientControlTask_Client.class.getName() ).log( Level.SEVERE, null, ex );
			}
		}
		else if ( ( dicFile == null && pieceInfo == null ) ) { //Client -> Server : Register Online 
			System.out.println( this.clientSocket.getLocalAddress() + " register online" );
			Message registerOnline = new Message( "GET", "SIGNIN" );
			out.println( registerOnline.toString() );
			try {
				waiting();
			}
			catch ( Exception ex ) {
				Logger.getLogger( ClientControlTask_Client.class.getName() ).log( Level.SEVERE, null, ex );
			}
		}
		closeConnection();

	}

	public synchronized void completeGettingAPiece() {
		this.isWaitingForPiece = false;
	}

	void requestStop() {
		this.isRunning = false;
	}
}
